﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
using Flame.Dlr;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace Flame.Controls
{

    //[ToolboxItem(false)]
    public partial class ScripterControl : UserControl, IScrollable
    {
        const int version = 1006;
        int _indexStartingfocused = -1;

        public Type DefaultLanguage { get; set; }


        List<int> _childMap = new List<int>();
        public  event EventHandler<KeyDownBubbleEventArgs> KeyDownBubble;
        public  event EventHandler<MouseEventArgs> MouseWheelFromChilds;
        public  event EventHandler OnMoveCaretOnScreen;
        public event EventHandler OnRefreshed; 
        public event EventHandler<Flame.Controls.TextInputWithInfo.TextInputPropertyChangedEventArgs> TextInputPropertyChanged;
        
        StringBuilder _trashStrings = new StringBuilder();

        public CalledCells CalledCells { get; private set; }

        public  Control Control { get { return this; } }

        public bool DefaultLanguageInheritFromLastChoosedLanguage { get; set; }

        private VerticalPanelScroll _verticalPanelScroll = null;
        //protected Manager _compilersManager;
        //Type _itextinputResolverType;
        TextInputCreator _textInputCreator;
        bool inserialization = false;


        public VerticalPanelScroll VerticalPanelScroll
        {
            get
            {
                if (_verticalPanelScroll == null)
                {
                    _verticalPanelScroll = VerticalPanelScroll.MakeScrollable(this);
                    _verticalPanelScroll.Dock = DockStyle.Fill;
                }
                return _verticalPanelScroll;
            }
        }

        public Manager Manager { get { return _textInputCreator.Manager; } }

        public OutputStreamWindow OutputStreamWindow { get; private set; }

        public ScripterControl(TextInputCreator textInputCreator, string defaultLanguage = "None")
        {
            //MessageBox.Show("ciao");
            //Debug.StartDebugger();
            //if (!DesignMode)
            {
                CalledCells = new CalledCells();

                _textInputCreator = textInputCreator;
                //_itextinputResolverType = itextinputResolverType;
                //_compilersManager = _textInputCreator.Manager;// new Manager(scopeParameters, availableLanguages);
                DefaultLanguage = Languages.Get(defaultLanguage);// Languages.None;

                Manager.ScopeParameters.AddAppDomainAssemblies();

                Manager.Clear();

                DefaultLanguageInheritFromLastChoosedLanguage = true;

                OutputStreamWindow = new OutputStreamWindow(Manager);

            }
            InitializeComponent();


        }


        private void ScripterControl_Load(object sender, EventArgs e)
        {
            if (!DesignMode)
            {
                if (Controls.Count == 0)
                {
                    _addNewInOut();
                }
                else
                {
                    for (int i = 0; i < _childMap.Count; i++)
                    {
                        if (((InOutControl)Controls[_childMap[i]]).InputAutoStart)
                            ((InOutControl)Controls[_childMap[i]]).CompileInput();
                    }
                }
                if (_indexStartingfocused > -1) ((InOutControl)Controls[_indexStartingfocused]).InputFocus();
                RefreshChilds();
                SizeChanged += (_s, _e) => { RefreshChilds(); };
            }
        }

        public void RefreshChilds()
        {
           this.SuspendLayout();
            int y = 0;
            foreach (int index in _childMap)
            {
                Control control = Controls[index];
                control.Location = new Point(0, y);
                control.Size = new Size(Width, control.Height);
                y += control.Height;
            }
            this.Height = y;
            this.ResumeLayout(false);
            foreach (int index in _childMap)
            {
                InOutControl x = (InOutControl)Controls[index];
                x.ChangeInPanelHeight();
            }
            if(OnRefreshed != null) OnRefreshed(this, new EventArgs());
        }


        void _remove(int i)
        {
            Controls[_childMap[i]].Visible = false;
            _childMap.RemoveAt(i);
        }
       
        int _findControlIndex(Control c)
        {
            for (int i = 0; i < _childMap.Count; i++)
            {
                if (Controls[_childMap[i]] == c)
                    return i;
            }
            return -1;
        }

        #region Edit Cell
        public void AddNewCellAfter(InOutControl inOutControl)
        {
            int i = _findControlIndex(inOutControl);
            if (i > -1)
            {
                _insertNewInOut(i + 1).Focus();
            }
            RefreshChilds();
        }
        public void CopyCellFromPrevious(InOutControl inOutControl)
        {
            int i = _findControlIndex(inOutControl);
            if (i > 0)
            {
                int sindex = ((InOutControl)Controls[_childMap[i]]).InputCurrentPos;
                string s = ((InOutControl)Controls[_childMap[i - 1]]).InputText;
                ((InOutControl)Controls[_childMap[i]]).InputText = ((InOutControl)Controls[_childMap[i]]).InputText.Insert(sindex, s);
                ((InOutControl)Controls[_childMap[i]]).InputCurrentPos = sindex + s.Length;
            }

            RefreshChilds();
        }
        public void CopyCellToNext(InOutControl inOutControl)
        {
            int i = _findControlIndex(inOutControl);
            if (i > -1)
            {
                _insertDuplicateInOut(i + 1).Focus();
            }

            RefreshChilds();
        }
        public void RemoveCell(InOutControl inOutControl)
        {
            int i = _findControlIndex(inOutControl);
            if (i > -1)
            {
                _remove(i);

                Controls[_childMap[i]].Focus();
            }
            RefreshChilds();
        }

        public void RemoveOutputCell(InOutControl inOutControl)
        {
            int i = _findControlIndex(inOutControl);
            if (i > -1)
            {
                ((InOutControl)Controls[_childMap[i]] ).Clear();
            }
            RefreshChilds();
        }

        public void ExecCell(InOutControl inOutControl)
        {
            int i = _findControlIndex(inOutControl);
            if (i > -1)
            {
                ((InOutControl)Controls[_childMap[i]]).Input.Compile();
            }
            RefreshChilds();
        }

        public void MoveDownCell(InOutControl inOutControl)
        {
            int i = _findControlIndex(inOutControl);
            if (i < _childMap.Count - 1)
            {
                Controls[_childMap[i]].Focus();
                _childMap.Reverse(i, 2);
                RefreshChilds();
            }
        }
        public void MoveUpCell(InOutControl inOutControl)
        {
            int i = _findControlIndex(inOutControl);
            if (i > 0)
            {
                Controls[_childMap[i]].Focus();
                _childMap.Reverse(i - 1, 2);
                RefreshChilds();
            }
        }

        public void NewCell() { AddNewCellAfter(CurrentCell); }
        public void CopyCellFromPrevious() { CopyCellFromPrevious(CurrentCell); }
        public void CopyCellToNext() { CopyCellToNext(CurrentCell); }
        public void RemoveCell() { RemoveCell(CurrentCell); }
        public void RemoveOutputCell() { RemoveOutputCell(CurrentCell); }
        public void MoveUpCell() { MoveUpCell(CurrentCell); }
        public void MoveDownCell() { MoveDownCell(CurrentCell); }
        public void ExecCell() { ExecCell(CurrentCell); }

        #endregion

        InOutControl _createNewInOut()
        {
            var textInputWithInfo = new TextInputWithInfo(_textInputCreator.CreateATextInput());//  new TextInputWithInfo((ATextInput)Activator.CreateInstance(_itextinputResolverType, Manager, ));

            textInputWithInfo.TextInputPropertyChanged += (s, e) => { if (TextInputPropertyChanged != null) TextInputPropertyChanged(this, e); };

            InOutControl x = new InOutControl(textInputWithInfo, new RichOutputCompiler());
            if (CurrentCell != null)
                x.InputLanguage = CurrentCell.Language;
            else
                x.InputLanguage = DefaultLanguage;
            x.OnChangeSize += (s, e) =>
            {
                RefreshChilds();
            };
            x.OnLanguageChanged += (s, e) =>
            {
                if(DefaultLanguageInheritFromLastChoosedLanguage)
                    DefaultLanguage = e.Language;
            };
            
            x.OnGotFocusChanged += (s, y) => 
            {
                _addIfLastHasFocus(y.InOutControl); 
            };
            x.AskForFocusToNext += (s, y) =>
            {
                bool found = false;
                foreach (int index in _childMap)
                {
                    Control control = Controls[index];
                    if (found)
                    {
                        control.Focus();
                        ((InOutControl)control).SetInputCusorBegin();
                        return;
                    }
                    if (control == y.InOutControl)
                        found = true;
                }
            };
            x.AskForFocusToPrevious += (s, y) =>
            {
                Control controlT = null;
                foreach (int index in _childMap)
                {
                    Control control = Controls[index];
                    if (control == y.InOutControl)
                    {
                        if (controlT != null)
                        {
                            controlT.Focus();
                            ((InOutControl)controlT).SetInputCusorEnd();
                        }
                        return;
                    }
                    controlT = control;
                }
            };

            x.MouseWheelFromChilds += (s, e) => { if (MouseWheelFromChilds != null) MouseWheelFromChilds(this, e); };
            x.OnMoveCaretOnScreen += (s, e) => { if (OnMoveCaretOnScreen != null) OnMoveCaretOnScreen(this, e); };
            x.KeyDownBubble += (s, e) => { if(KeyDownBubble != null) KeyDownBubble(this, e); };
            x.OnCompile += (s, e) =>
            {
                CalledCells.Add(s as InOutControl);
            };
            return x;
        }

        InOutControl _addNewInOut()
        {
            InOutControl x = _createNewInOut();
            Controls.Add(x);
            _childMap.Add(Controls.Count - 1);
            return x;
        }
        InOutControl _insertNewInOut(int pos)
        {
            InOutControl x = _createNewInOut();
            Controls.Add(x);
            _childMap.Insert(pos, Controls.Count - 1);
            return x;
        }
        InOutControl _insertDuplicateInOut(int pos)
        {
            InOutControl x = _createNewInOut();
            Controls.Add(x);
            x.InputText = ((InOutControl)Controls[_childMap[pos - 1]]).InputText;
            x.InputCurrentPos = ((InOutControl)Controls[_childMap[pos - 1]]).InputCurrentPos;
            x.InputLanguage = ((InOutControl)Controls[_childMap[pos - 1]]).InputLanguage;
            _childMap.Insert(pos, Controls.Count - 1);
            return x;
        }

        void _addIfLastHasFocus(InOutControl x)
        {
            if (inserialization) return;
            if (Controls.Count == 0)
            {
                _addNewInOut();
                RefreshChilds();
            }
            else
            {
                if (Controls[_childMap[_childMap.Count - 1]] == x)
                {
                    _addNewInOut();
                    RefreshChilds();
                }
            }
        }

        public IEnumerable<ScriptBrute> Cells
        {
            get
            {
                return from y in
                                (from x in _childMap select (InOutControl)Controls[x])
                            select (new Cell( y.InputLanguage, y.InputText,  y.InputAutoStart)).Brute;
            }
            set
            {
                SuspendLayout();
                inserialization = true;
                Controls.Clear();
                _childMap.Clear();

                foreach (var bcell in value)
                {
                    Cell cell = new Cell(bcell);
                    InOutControl inOutControl = _addNewInOut();
                    inOutControl.InputLanguage = cell.Language;
                    inOutControl.InputText = cell.Code;
                    inOutControl.InputAutoStart = cell.Auto;
                    inOutControl.RefreshHeights();
                }

                RefreshChilds();
                inserialization = false;
                ResumeLayout(false);
            }
        }

        public InOutControl CurrentCell
        {
            get
            {
                foreach (int index in _childMap)
                {
                    InOutControl control = (InOutControl)Controls[index];
                    if (control.InputFocused)
                        return control;
                }

                return null;
            }
        }

        public  int CaretOnScreenPosY 
        {
            get
            {
                InOutControl c = CurrentCell;
                if (c == null) return 0;
                return c.InputCaretOnScreenPosY;
            }
        }

        public  void SaveState()
        {
            foreach (int index in _childMap)
            {
                ((InOutControl)Controls[index]).SaveState();
            }
        }

        public  bool HasStateChanged
        {
            get
            {
                foreach (int index in _childMap)
                {
                    if (((InOutControl)Controls[index]).HasStateChanged) return true;
                }
                return false;
            }
        }
    }
}
